package com.bootlever.framework.rmi.listener;

import com.alibaba.fastjson.JSON;
import com.bootlever.framework.common.utils.StringUtils;
import com.bootlever.framework.rmi.annotation.RmiServer;
import com.bootlever.framework.rmi.annotation.RmiServerResource;
import com.bootlever.framework.rmi.bean.BootleverRmiContainer;
import com.bootlever.framework.rmi.bean.RmiClient;
import com.bootlever.framework.rmi.bean.RmiInfo;
import com.bootlever.framework.rmi.constant.RmiConstant;
import com.bootlever.framework.rmi.constant.RmiServerStatus;
import com.bootlever.framework.rmi.constant.RmiZkConstant;
import com.bootlever.framework.zookeeper.templates.ZookeeperTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import javafx.scene.effect.Reflection;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.MethodParameterNamesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;


/**
 * 监听服务变化
 */
public class RmiServerConfigListener implements ApplicationListener<ContextRefreshedEvent>,ApplicationContextAware,
        RmiZkConstant, RmiConstant{

    private static final Logger logger = LoggerFactory.getLogger(RmiServerConfigListener.class);

    private ApplicationContext applicationContext;

    @Autowired
    private ZookeeperTemplate zookeeperTemplate;

    @Autowired
    private BootleverRmiContainer rmiContainer;

    private Executor executor = Executors.newFixedThreadPool(10);

    private Object lock = new Object();


    public void onApplicationEvent(ContextRefreshedEvent event) {
        logger.info("注册rmi zookeeper监听器");
        try {
            Map<String, RmiClient> clientMap = rmiContainer.getClientContainer();
            List<String> nodes = Lists.newCopyOnWriteArrayList();
            for (RmiClient rmiClient : clientMap.values()){
                String appName = rmiClient.getAppName();
                Set<String> set = Sets.newHashSet();
                set.addAll(rmiClient.getOkStatusInfos().keySet());
                set.addAll(rmiClient.getBusyStatusInfos().keySet());
                for (String key : set){
                    StringBuffer sb = new StringBuffer(100);
                    sb.append(RmiZkConstant.ROOT_PATH).append("/").append(appName).append("/").append(key);
                    String nodePath = sb.toString().intern();
                    if (!nodes.contains(nodePath)){
                        nodes.add(nodePath);
                    }
                }
            }
            if (nodes != null && !nodes.isEmpty()){
                for (final String path : nodes){
                    //只监控下一级
                    PathChildrenCache pathChildrenCache = zookeeperTemplate.createPathChildrenCache(path);
                    pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
                        @Override
                        public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                            switch (event.getType()) {
                                case CHILD_ADDED:
                                    logger.info("CHILD_ADDED :{}",event.getData().getPath());
                                    //resetRmiInfo();
                                    reset();
                                    break;
                                case CHILD_UPDATED:
                                    logger.info("CHILD_UPDATED :{}",event.getData().getPath());
                                    //resetRmiInfo();
                                    reset();
                                    break;
                                case CHILD_REMOVED:
                                    logger.info("CHILD_REMOVED :{}", event.getData().getPath());
                                    //resetRmiInfo();
                                    reset();
                                    break;
                                default:
                                    break;
                            }
                        }

                        private void reset(){
                            executor.execute(new Runnable() {
                                @Override
                                public void run() {
                                    resetRmiInfo();
                                }
                            });
                        }

                        /**
                         * 每次改变都刷新一遍状态比较保险
                         */
                        private void resetRmiInfo() {
                            synchronized (lock){
                                try {
                                    String[] paths = path.split("/");
                                    String appName = paths[2];
                                    String name = paths[3];
                                    List<String> childrenNodes = zookeeperTemplate.getChildren(path);
                                    List<RmiInfo> okList = Lists.newCopyOnWriteArrayList();
                                    List<RmiInfo> busyList = Lists.newCopyOnWriteArrayList();
                                    for (String p : childrenNodes) {
                                        StringBuffer sb = new StringBuffer(100);
                                        sb.append(path).append("/").append(p);
                                        String data = zookeeperTemplate.getData(sb.toString());
                                        RmiInfo rmiInfo = JSON.parseObject(data, RmiInfo.class);
                                        if (RmiServerStatus.OK.name().equals(rmiInfo.getStatus())){
                                            okList.add(rmiInfo);
                                        } else if (RmiServerStatus.BUSY.name().equals(rmiInfo.getStatus())){
                                            busyList.add(rmiInfo);
                                        }
                                    }
                                    rmiContainer.getClient(appName).getOkStatusInfos().put(name, okList);
                                    rmiContainer.getClient(appName).getBusyStatusInfos().put(name,busyList);
                                    if (logger.isInfoEnabled()){
                                        logger.info("刷新后rmiContainer client:{}",JSON.toJSON(rmiContainer.getClientContainer()));
                                    }
                                } catch (Exception e) {
                                    logger.error("reset rmiInfos 失败", e);
                                }
                            }
                        }
                    });
                    logger.info("启动对{}节点的rmi服务监控",path);
                }
            }
        } catch (Exception e) {
            logger.info("rmi监听失败", e);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
